//	--------------------------------------------------------------------
//	Copyright(C) 2006,2007 Zhao Yukun. All rights reserved.
//	This file is a part of the Xeres system.
//	Xeres 2007
//	Contact : xeres.engine@gmail.com
//	$(reservedInfo)
//	$(reservedDeclaration)
//	--------------------------------------------------------------------
///	@file	<time.inl>
///	@path	~/src/utilities/
///	@date	2007/06/14
///	@desc	time function inlines.

#pragma once

#include "config/os.h"
#include "lib/utilities/time.h"

namespace xeres {

	// time

	// ctor
	inline Time::Time( void ) : m_time( 0 )
	{
	}

	// copier
	inline Time::Time( const Time& rhs ) : m_time( rhs.m_time )
	{
	}

	// time stamp
	inline Time::Time( const TimeStamp& timeStamp ) : m_time( timeStamp )
	{
	}

	// differ time
	inline Time::Time( const Time& start , const Time& end )
	{
		m_time = abs( end.m_time - start.m_time );
	}

	// formate time as partition
	inline void Time::PartitionTime( TimePartition& part ) const
	{
		// zero partition
		memset( &part , 0 , sizeof(TimePartition ) );
		
		// extract time info
		time_t msec = m_time;
		if( msec >= 1000 )
		{
			// normalize to second
			time_t sec = ( msec / 1000 );
			msec = msec % 1000;

			if( sec >= 60 )
			{
				// normalize to minute
				time_t minute = ( sec / 60 );
				sec = sec % 60;

				if( minute >= 60 )
				{
					// normalize to hour
					time_t hour = ( minute / 60 );
					minute = minute % 60;

					if( hour >= 24 )
					{
						// normalize to day
						time_t day = ( hour / 24 );
						hour = hour % 24;
						part.nDay = (int)day;
					}
					part.nMinute = (int)minute;
				}
				part.nSecond = (int)sec;
			}
		}
		part.nMilliSecond = (int)msec;
	}

#if CLOCKS_PER_SEC == 1000
	// fits millisecond unit
#	define	CLOCKS_TO_MILLISECOND		(1)
#else
#pragma message( "CLOCKS PER SECOND does not match 1000, need extra calculation." )
	// dont' fits
#	define	CLOCKS_TO_MILLISECOND		( (float)CLOCKS_PER_SEC / 1000.f )
#endif

	// to millisecond
	inline int64 Time::ToMilliSecond( void ) const
	{
		return (int64)( m_time * CLOCKS_TO_MILLISECOND );
	}

	// to millisecond
	inline void Time::ToMilliSecond( int64& m ) const
	{
		m = (int64)( m_time * CLOCKS_TO_MILLISECOND);
	}

	// to millisecond
	inline void Time::FromMilliSecond( int64 m )
	{
		m_time = (TimeStamp) ( m / CLOCKS_TO_MILLISECOND );
	}

	// assign time
	inline Time& Time::operator = ( const Time& rhs )
	{
		m_time = rhs.m_time;
		return *this;
	}

	// assign time
	inline Time& Time::operator = ( const TimeStamp& timeStamp )
	{
		m_time = timeStamp;
		return *this;
	}

	// add 2 'Time'
	inline Time operator + ( const Time& lval , const Time& rhs )
	{
		return Time( lval.m_time + rhs.m_time );
	}

	// differ 2 'Time'
	inline Time operator - ( const Time& lval , const Time& rhs )
	{
		return Time( abs( lval.m_time - rhs.m_time ) );
	}

	// compare 2 'Time' (longer or shorter)
	inline bool operator < ( const Time& lval , const Time& rhs )
	{
		return lval.m_time < rhs.m_time;
	}

	// compare 2 'Time' (longer or shorter)
	inline bool operator > ( const Time& lval , const Time& rhs )
	{
		return lval.m_time > rhs.m_time;
	}

	// compare 2 'Time' (the same?)
	inline bool operator == ( const Time& lval , const Time& rhs )
	{
		return lval.m_time == rhs.m_time;
	}

	// compare 2 'Time' (not the same?)
	inline bool operator != ( const Time& lval , const Time& rhs )
	{
		return lval.m_time != rhs.m_time;
	}


	// get current time
	inline Time Time::Now( void )
	{
		return Time( ::clock() );
	}

	inline void Time::Now( Time& t )
	{
		t.m_time = ::clock();
	}

	// counter

	// ctor
	inline Counter::Counter( void ) : m_counter( 0 )
	{
	}

	// copier
	inline Counter::Counter( const Counter& rhs ) : m_counter( rhs.m_counter )
	{
	}

	// counter stamp
	inline Counter::Counter( const CounterStamp& counterStamp ) : m_counter( counterStamp )
	{
	}

	// differ counter
	inline Counter::Counter( const Counter& start , const Counter& end )
	{
		m_counter = end.m_counter - start.m_counter;
	}

	static inline long double __getCounterFreq( void )
	{
		static bool run = false;
		static long double dFreq = 0;
		if( !run )
		{
			// get frequency (notice frequency is in second)
			int64 freq = 0;
			::QueryPerformanceFrequency( (LARGE_INTEGER*) &freq );
			// make million second
			dFreq = (long double) freq / 1000.0;
		}
		return dFreq;
	}

	// convert 'Counter' to 'Time'
	inline Time Counter::ToTime( void ) const
	{
		const long double tt = (long double)m_counter / __getCounterFreq();
		return Time( (TimeStamp) ( tt ) );
	}

	// convert 'Counter' to 'Time'
	inline void Counter::ToTime( Time& t ) const
	{
		t = ( (TimeStamp) ( m_counter / __getCounterFreq() ) );
	}

	// Convert to second.
	inline long double Counter::ToSecond( void ) const
	{
		return (long double)m_counter / (long double)__getCounterFreq() / 1000.0;
	}

	// Convert to milli-second.
	inline double Counter::ToMilliSecond( void ) const
	{
		return double( (long double)m_counter / (long double)__getCounterFreq() );
	}

	// assign time
	inline Counter& Counter::operator = ( const Counter& rhs )
	{
		m_counter = rhs.m_counter;
		return *this;
	}

	// assign time
	inline Counter& Counter::operator = ( const CounterStamp& timeStamp )
	{
		m_counter = timeStamp;
		return *this;
	}

	// add 2 'Time'
	inline Counter operator + ( const Counter& lval , const Counter& rhs )
	{
		return Counter( lval.m_counter + rhs.m_counter );
	}

	// differ 2 'Time'
	inline Counter operator - ( const Counter& lval , const Counter& rhs )
	{
		return Counter( abs( (TimeStamp)( lval.m_counter - rhs.m_counter ) ) );
	}

	// compare 2 'Time' (longer or shorter)
	inline bool operator < ( const Counter& lval , const Counter& rhs )
	{
		return lval.m_counter < rhs.m_counter;
	}

	// compare 2 'Time' (longer or shorter)
	inline bool operator > ( const Counter& lval , const Counter& rhs )
	{
		return lval.m_counter > rhs.m_counter;
	}

	// compare 2 'Time' (the same?)
	inline bool operator == ( const Counter& lval , const Counter& rhs )
	{
		return lval.m_counter == rhs.m_counter;
	}

	// compare 2 'Time' (not the same?)
	inline bool operator != ( const Counter& lval , const Counter& rhs )
	{
		return lval.m_counter != rhs.m_counter;
	}


	// get current counter
	inline Counter Counter::Now( void )
	{
		int64 c = 0;
		::QueryPerformanceCounter( (LARGE_INTEGER*) &c );
		return Counter( c );
	}

	inline void Counter::Now( Counter& c )
	{
		::QueryPerformanceCounter( (LARGE_INTEGER*) &(c.m_counter) );
	}

	// date time

	// ctor
	inline DateTime::DateTime( void )
		: m_time( 0 )
	{
	}

	// copier
	inline DateTime::DateTime( const DateTime& rhs )
		: m_time( rhs.m_time )
	{
	}

	// time stamp
	inline DateTime::DateTime( const DateTimeStamp& dateTimeStamp )
		: m_time( dateTimeStamp )
	{
	}

	// is null
	inline bool DateTime::IsNull( void ) const
	{
		return m_time == NULL;
	}

	// format time as partition
	inline void DateTime::PartitionDateTime( DateTimePartition& part ) const
	{
		tm t;
		::localtime_s( &t , &m_time );

		part.nYear = t.tm_year + 1900;
		part.nMonth = t.tm_mon + 1;
		part.nDay = t.tm_mday;
		part.nHour = t.tm_hour;
		part.nMinute = t.tm_min;
		part.nSecond = t.tm_sec;
		part.nWeekday = t.tm_wday;
	}

	// make time from partition
	inline void DateTime::FromPartition( const DateTimePartition& part )
	{
		tm t;
		t.tm_year = part.nYear - 1900;
		t.tm_mon = part.nMonth - 1;
		t.tm_mday = part.nDay;
		t.tm_hour = part.nHour;
		t.tm_min = part.nMinute;
		t.tm_sec = part.nSecond;
		t.tm_wday = part.nWeekday;
		m_time = mktime( &t );
	}

	// assign time
	inline DateTime& DateTime::operator = ( const DateTime& rhs )
	{
		m_time = rhs.m_time;
		return *this;
	}

	// assign time
	inline DateTime& DateTime::operator = ( const DateTimeStamp& timeStamp )
	{
		m_time = timeStamp;
		return *this;
	}

	// compare 2 'Time' (the same?)
	inline bool operator == ( const DateTime& lval , const DateTime& rhs )
	{
		return lval.m_time == rhs.m_time;
	}

	// compare 2 'Time' (not the same?)
	inline bool operator != ( const DateTime& lval , const DateTime& rhs )
	{
		return lval.m_time != rhs.m_time;
	}
	
	// Convert time stamp.
	inline DateTime::operator DateTimeStamp ( void )
	{
		return m_time;
	}

	// get current time
	inline DateTime DateTime::Now( void )
	{
		return DateTime( time( NULL ) );
	}

	inline void DateTime::Now( DateTime& t )
	{
		t.m_time = time( NULL );
	}

} // namespace xeres
